# -*- coding: utf-8 -*-

from flask import render_template, g, jsonify, abort, redirect, request
from functools import wraps

from .exceptions import FrozenPropertyError


def POST(f):
    """ """
    @wraps(f)
    def inner(*args, **kwargs):
        if request.method == 'POST':
            return f(*args, **kwargs)
        else:
            abort(405)
    return inner


def GET(f):
    """ """
    @wraps(f)
    def inner(*args, **kwargs):
        if request.method == 'GET':
            return f(*args, **kwargs)
        else:
            abort(405)
    return inner


def login_required_decor_factory(user_predicate, login_url_builder):
    """
    Factory function making login required decorator.
    
    :user_predicate:
    Callable that must return Trueable value if user is logged in and Falsable value otherwise.
    
    :login_url_builder:
    Callable that should return login page url.
    
    If request is not authenticated than user agent is redirected to login page.
    
    Example use:
    
    login_required = login_required_decor_factory(lambda: g.user, create_login_url)
    """
    def outer(f):
        @wraps(f)
        def inner(*args, **kwargs):
            if not user_predicate():
                return redirect(login_url_builder())
            return f(*args, **kwargs)
        return inner
    return outer


def anonymous_required_decor_factory(user_predicate, action):
    """
    Factory function making login required decorator.
    
    :user_predicate:
    Callable that must return Trueable value if user is logged in and Falsable value otherwise.
    
    :login_url_builder:
    Callable that should return some response or raise some exception.
    
    If request is not authenticated than user agent is redirected to login page.
    
    Example use:
    
    anonymous_required = anonymous_required_decor_factory(
            lambda: g.user, 
            lambda: abort(403, 'You are already logged in.')
        )
    """
    def outer(f):
        @wraps(f)
        def inner(*args, **kwargs):
            if user_predicate():
                return action()
            return f(*args, **kwargs)
        return inner
    return outer


def authorize_decor_factory(user_predicate, action):
    """
    Factory function making authorize decorator.
    
    :user_predicate:
    Callable that must return Trueable value if user is authorized to perform action of Falsable otherwise.
    
    :action:
    Callable that should return some response or raise some exception.
    
    If request is not authenticated than user agent is redirected to login page.
    
    Example use:
    
    admin_required = authorize_decor_factory(
            lambda: g.user in ADMIN_LIST, 
            lambda: abort(403, 'You are already logged in.')
        )
    """
    def dec(f):
        @wraps(f)
        def inner(*args, **kwargs):
            if not user_predicate():
                return action()
            return f(*args, **kwargs)
        return inner
    return dec


def anonymous_required(f):
    """
    Anonymous decorator.
    """
    @wraps(f)
    def inner(*args, **kwargs):
        if g.user:
            abort(403, 'You are already logged in.')
        return f(*args, **kwargs)
    return inner 


def json_it(transformer=None):
    """ """
    def outer(f):
        if transformer:
            @wraps(f)
            def inner(*args, **kw):
                return jsonify(transformer(f(*args, **kw)))
            return inner
        else:
            @wraps(f)
            def inner(*args, **kw):
                return jsonify(f(*args, **kw))
            return inner
    return outer


def html_it(tmpl, OK_redir_or_tmpl=None):
    """
    `tmpl` - main template with html form. It is rendered during GET-request and POST-request when 
    submitted form is invalid.
    
    `OK_redir_or_tmpl` - this is eiher template name or callable returning URL. Fired during POST-request
    when everything is submittid without errors.
    """
    def outer(f):
        @wraps(f)
        def inner(*args, **kw):
            
            method = request.method
            res = f(*args, **kw)
            
            if method == 'POST' and 'errors' not in res:
                if isinstance(OK_redir_or_tmpl, basestring):
                    return render_template(OK_redir_or_tmpl, **res)
                else:
                    return redirect(OK_redir_or_tmpl(**res)) 
                       
            return render_template(tmpl, **res)
        return inner
    return outer


def glue_GET_POST(get, post):
    """ """
    # XXX check that functions signatures are the same
    
    def out(*args, **kwargs):
        method = request.method
        if method == 'GET':
            return get(*args, **kwargs)
        elif method == 'POST':
            return post(*args, **kwargs)
        else:
            abort(403)
    return out


def frozen_property(property_class):
    """
    """
    class FrozenProperty(property_class):
        """ """
        def __set__(self, model_instance, value):
            if model_instance.is_saved():
                raise FrozenPropertyError
            else:
                return property_class.__set__(self, model_instance, value)
    
    return FrozenProperty
